package com.jumi.microservice.stock.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.jumi.microservice.common.base.dto.ResponseResult;
import com.jumi.microservice.common.constant.IsDeletedEnum;
import com.jumi.microservice.common.constant.StockChangeTypeEnum;
import com.jumi.microservice.common.constant.StockOccupyIsReleasedEnum;
import com.jumi.microservice.common.constant.StockOccupyTypeEnum;
import com.jumi.microservice.common.exception.ExceptionEnum;
import com.jumi.microservice.common.utils.StringProvider;
import com.jumi.microservice.material.domain.JmDictValue;
import com.jumi.microservice.material.domain.JmGoods;
import com.jumi.microservice.material.mapper.JmGoodsMapper;
import com.jumi.microservice.material.service.JmDictValueService;
import com.jumi.microservice.stock.api.dto.CommodityStockReleaseDTO;
import com.jumi.microservice.stock.domain.*;
import com.jumi.microservice.stock.dto.StockMaterialStockDTO;
import com.jumi.microservice.stock.mapper.*;
import com.jumi.microservice.stock.service.StockService;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/*import com.jumi.microservice.stock.dto.StockLogIdentificationDO;*/

/**
 * @ClassName StockServiceImpl
 * @Description 仓库库存
 * @Author Abel Yang
 * @Date 2020/8/25/025 15:23
 **/
@Service
@Transactional(rollbackFor = Exception.class)
public class StockServiceImpl implements StockService {
    private static final Logger log = LoggerFactory.getLogger(StockServiceImpl.class);
    @Resource
    private StockMaterialMapper stockMaterialMapper;

    @Resource
    private StockOccupyMapper stockOccupyMapper;

    @Resource
    private StockOccupyDamageMapper stockOccupyDamageMapper;

    @Resource
    private StockOccupyOrderMapper stockOccupyOrderMapper;

    @Resource
    private StockLogMapper stockLogMapper;

    @Resource
    private JmGoodsMapper goodsMapper;

    @Resource
    private JmDictValueService dictValueService;

    @Override
    public ResponseResult inStock(StockDTO stockDTO) {

        try {
            ExceptionEnum.FAIL_STOCK_NUM_IS_NOT_EMPTY.doThrowIf(null == stockDTO.getInStockTotalNum() && null == stockDTO.getInStockDamageTotalNum());
            //详情校验
            List<StockDetailDTO> detailDtoList = stockDTO.getStockDetailDtoList();
            ExceptionEnum.FAIL_STOCK_DETAIL_LIST_IS_NOT_EMPTY.doThrowIf(0 == detailDtoList.size());

            if (null == stockDTO.getOutStockTotalNum()) {
                stockDTO.setOutStockTotalNum(0);
            }
            if (null == stockDTO.getOutStockDamageTotalNum()) {
                stockDTO.setOutStockDamageTotalNum(0);
            }

            //入库总数校验
            int detailInStockTotalNum =
                    detailDtoList.stream().filter(k -> k.getInStockNum() != null).mapToInt(StockDetailDTO::getInStockNum).sum();
            ExceptionEnum.FAIL_STOCK_NUM_NOT_MATCH.doThrowIf(null != stockDTO.getInStockTotalNum()
                    && detailInStockTotalNum != stockDTO.getInStockTotalNum());
            int detailInStockDamageTotalNum =
                    detailDtoList.stream().filter(k -> k.getInStockDamageNum() != null).mapToInt(StockDetailDTO::getInStockDamageNum).sum();
            ExceptionEnum.FAIL_STOCK_NUM_NOT_MATCH.doThrowIf(null != stockDTO.getInStockDamageTotalNum()
                    && detailInStockDamageTotalNum != stockDTO.getInStockDamageTotalNum());

            //入库存
            doInStock(stockDTO);
        } catch (Exception e) {
            return ResponseResult.error(e.getMessage());
        }
        return ResponseResult.success("操作成功", true);
    }

    /**
     * @Description: 商品库存入库
     * @Param stockDTO
     * @Return
     * @Throws
     * @Author Abel Yang
     * @Date 2020/9/1/001 14:01
     */
    private void doInStock(StockDTO stockDTO) {

        //查询商品
        if (StringProvider.isNull(stockDTO.getGoodsCode())) {
            JmGoods goods = goodsMapper.selectOne(Wrappers.<JmGoods>lambdaQuery()
                    .select(JmGoods::getGoodsId, JmGoods::getGoodsCode)
                    .eq(JmGoods::getGoodsId, stockDTO.getGoodsId()));
            stockDTO.setGoodsCode(goods.getGoodsCode());
        }

        //仓库物料入库
        //遍历入库存
        stockDTO.getStockDetailDtoList().forEach(detailDTO -> {
            Integer inStockNums = 0;
            if (null == detailDTO.getInStockNum()) {
                detailDTO.setInStockNum(0);
            }
            if (null == detailDTO.getInStockDamageNum()) {
                detailDTO.setInStockDamageNum(0);
            }
            //如果正规仓数量有值
            if (detailDTO.getInStockNum() > detailDTO.getInStockDamageNum()) {
                inStockNums = detailDTO.getInStockNum();
            } else {
                inStockNums = detailDTO.getInStockDamageNum();
            }

            //物料变动前原始库存
            int originNum = 0;
            //查询仓库物料库存记录
            StockMaterialDO stockMaterial = getStockMaterialDO(detailDTO.getMaterialId(), stockDTO.getWarehouseId());
            if (null != stockMaterial) {
                originNum = detailDTO.getInStockNum() > 0 ? stockMaterial.getRealStockNum() :
                        stockMaterial.getDamageStockNum();
            }

            doMaterialInStock(stockDTO, detailDTO, stockMaterial);
            //如果是供应链端一般仓库入库则写日志
            if (null != stockDTO.getIsGeneralWarehouse() && stockDTO.getIsGeneralWarehouse() == 1) {
                //入库存日志
                String changeReason = "";
                try {
                    Long valId = Long.parseLong(stockDTO.getChangeType().toString());
                    JmDictValue dictValue = dictValueService.findById(valId);
                    changeReason = dictValue.getValName();
                    if (StringUtils.isBlank(changeReason)) {
                        changeReason = "不明原因";
                    } else {
                        changeReason = changeReason;
                    }
                } catch (NumberFormatException e) {
                    changeReason = "不明原因";
                }
                String backUps = changeReason + stockDTO.getChangeReasonId();
                byte isDamage = detailDTO.getInStockDamageNum() > 0 ? (byte) 2 : (byte) 0;
                logAdd(detailDTO.getMaterialId(), stockDTO.getGoodsId(), stockDTO.getWarehouseId(),
                        stockDTO.getChangeType(), inStockNums, (byte) 1, stockDTO.getBatchCode(),
                        stockDTO.getUpdateBy(),
                        isDamage, originNum, backUps);
            }
        });
    }

    private void doMaterialInStock(StockDTO stockDTO, StockDetailDTO detailDTO, StockMaterialDO stockMaterial) {
        //物料总入库量
        int inStockTotalNum = detailDTO.getInStockNum() + detailDTO.getInStockDamageNum();

        if (null != stockMaterial) {
            LambdaUpdateWrapper<StockMaterialDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper
                    .setSql("in_stock_num = in_stock_num + " + inStockTotalNum)
                    .setSql("real_stock_num = real_stock_num + " + inStockTotalNum)
                    .set(StockMaterialDO::getUpdateBy, stockDTO.getUpdateBy())
                    .eq(StockMaterialDO::getId, stockMaterial.getId())
                    //可用库存充足解除报警
                    .set(StockMaterialDO::getAlarmed,
                            stockMaterial.getAlarmNum() > (stockMaterial.getAvailableStockNum() + detailDTO.getInStockNum()));

            // 判断入库是正常商品入库，还是破损入库
            if (detailDTO.getInStockNum() > 0) {
                updateWrapper.setSql("available_stock_num = available_stock_num + " + detailDTO.getInStockNum());
            } else {
                updateWrapper.setSql("damage_stock_num = damage_stock_num + " + detailDTO.getInStockDamageNum());
            }

            stockMaterialMapper.update(new StockMaterialDO(), updateWrapper);
        } else {
            StockMaterialDO materialDO = new StockMaterialDO();
            materialDO.setMaterialId(detailDTO.getMaterialId());
            materialDO.setMaterialCode(detailDTO.getMaterialCode());
            materialDO.setGoodsId(stockDTO.getGoodsId());
            materialDO.setGoodsCode(stockDTO.getGoodsCode());
            materialDO.setWarehouseId(stockDTO.getWarehouseId());
            materialDO.setInStockNum(inStockTotalNum);
            materialDO.setRealStockNum(inStockTotalNum);
            materialDO.setAvailableStockNum(detailDTO.getInStockNum());
            materialDO.setDamageStockNum(detailDTO.getInStockDamageNum());
            materialDO.setCreateBy(stockDTO.getUpdateBy());
            materialDO.setUpdateBy(stockDTO.getUpdateBy());
            stockMaterialMapper.insert(materialDO);
        }
    }

    @Override
    public ResponseResult occupyStock(StockOccupy4CommodityDTO occupyDTO) {

        //商城申请库存-常规占用
        StockOccupyDO occupyDO = new StockOccupyDO();
        occupyDO.setMaterialId(occupyDTO.getMaterialId());
        occupyDO.setWarehouseId(occupyDTO.getWarehouseId());
        occupyDO.setOccupyNum(occupyDTO.getOccupyNum());
        occupyDO.setCreateBy(occupyDTO.getUpdateBy());
        occupyDO.setUpdateBy(occupyDTO.getUpdateBy());

        //库存变动日志
        normalOccupy(occupyDO, StockChangeTypeEnum.TYPE_COMMODITY_APPLY.getChangeType());

        return ResponseResult.success();
    }

    @Override
    public ResponseResult occupyStock(StockOccupy4WarehouseDTO occupyDTO) {
        log.info("---------排查售后审核问题----[传递值]:[{}]", JSON.toJSONString(occupyDTO));
        //仓库常规占用
        StockOccupyDO occupyDO = new StockOccupyDO();
        Integer changeType = null;
        changeType = occupyDTO.getChangeType();
        //仓库审核售后订单-占用-需要售后单号
        if (StringProvider.isNotEmpty(occupyDTO.getOrderSn())) {
            occupyDO.setAfterSaleOrderSn(occupyDTO.getOrderSn());
            changeType = StockChangeTypeEnum.TYPE_WAREHOUSE_OUT_AFTER_SALE_ORDER.getChangeType();
        }
        //公用出库申请-占用-需要出库申请id
        if (null != occupyDTO.getOutStockApplyId()) {
            occupyDO.setOutStockApplyId(occupyDTO.getOutStockApplyId());
            changeType = occupyDTO.getChangeType();
        }

        //破损出库占用
        if (true == occupyDTO.getIsDamage()) {
            log.info("---------排查售后审核问题----[破损出库占用]:[{}][{}]", JSON.toJSONString(occupyDTO.getIsDamage()),
                    JSON.toJSONString(occupyDTO.getOrderSn()));
            damageOccupy(occupyDTO, changeType);
            return ResponseResult.success();
        }
        occupyDO.setMaterialId(occupyDTO.getMaterialId());
        occupyDO.setWarehouseId(occupyDTO.getWarehouseId());
        occupyDO.setOccupyNum(occupyDTO.getOccupyNum());
        occupyDO.setOccupyType(StockOccupyTypeEnum.TYPE_WAREHOUSE.getType());
        occupyDO.setCreateBy(occupyDTO.getUpdateBy());
        occupyDO.setUpdateBy(occupyDTO.getUpdateBy());

       /* StockLogIdentificationDO stockLogIdentificationDO = new StockLogIdentificationDO();
        stockLogIdentificationDO.setChangeReasonId(occupyDTO.getChangeReasonId());*/

        normalOccupy(occupyDO, changeType);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult occupyStockRelease4PublicApply(StockOccupy4WarehouseDTO occupyDTO) {

        //破损释放
        if (occupyDTO.getIsDamage()) {
            occupyReleaseDamage(occupyDTO);
            return ResponseResult.success();
        }
        //常规占用释放(公共出库)
        LambdaQueryWrapper<StockOccupyDO> occupyWrapper = Wrappers.lambdaQuery();
        occupyWrapper.eq(StockOccupyDO::getOutStockApplyId, occupyDTO.getOutStockApplyId());
        occupyReleaseApply(occupyDTO, occupyWrapper);


        return ResponseResult.success();
    }

    /**
     * type 1:公用出库审核未通过  2：售后
     *
     * @param occupyDTO
     * @param type
     * @return
     */
    @Override
    public ResponseResult occupyStockRelease2Status(StockOccupy4WarehouseDTO occupyDTO, Integer type) {
        //常规占用释放(公共出库)
        LambdaQueryWrapper<StockOccupyDO> occupyWrapper = Wrappers.lambdaQuery();
        if (type == 1) {
            occupyWrapper.eq(StockOccupyDO::getOutStockApplyId, occupyDTO.getOutStockApplyId())
                    .eq(StockOccupyDO::getIsReleased, 0)
                    .eq(StockOccupyDO::getIsDel, 0)
                    .gt(StockOccupyDO::getOccupyNum, 0);
        } else {
            occupyWrapper.eq(StockOccupyDO::getAfterSaleOrderSn, occupyDTO.getAfterSaleOrderSn())
                    .eq(StockOccupyDO::getIsReleased, 0)
                    .eq(StockOccupyDO::getIsDel, 0)
                    .gt(StockOccupyDO::getOccupyNum, 0);
        }
        List<StockOccupyDO> stockOccupyDOS = stockOccupyMapper.selectList(occupyWrapper);
        ExceptionEnum.FAIL_STOCK_OCCUPY_RECORD_NOT_EXISTS.doThrowIf(null == stockOccupyDOS);
        for (StockOccupyDO stockOccupy : stockOccupyDOS) {
            //StockOccupyDO stockOccupy = stockOccupyMapper.selectOne(occupyWrapper);
            ExceptionEnum.FAIL_STOCK_OCCUPY_RECORD_NOT_EXISTS.doThrowIf(null == stockOccupy);
            //查询物料库存记录
            StockMaterialDO stockMaterial = getStockMaterialDO(stockOccupy.getMaterialId(),
                    stockOccupy.getWarehouseId());
            ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);
            //物料商品占用更新
            assert stockMaterial != null;
            assert stockOccupy != null;
            //更新物料库存 减占用
            int materialOccupyLeft = stockMaterial.getOccupyNum() - stockOccupy.getOccupyNum();
            ExceptionEnum.FAIL_STOCK_OCCUPY_MATERIAL_OCCUPY_NUM_NOT_ENOUGH.doThrowIf(materialOccupyLeft < 0);
            stockMaterialMapper.update(
                    new StockMaterialDO(),
                    Wrappers.<StockMaterialDO>lambdaUpdate()
                            .eq(StockMaterialDO::getId, stockMaterial.getId())
                            .setSql("occupy_num = occupy_num -" + stockOccupy.getOccupyNum())
                            .setSql("available_stock_num = available_stock_num +" + stockOccupy.getOccupyNum())
            );
            //清除占用记录上的占用标记
            stockOccupy.setIsReleased(StockOccupyIsReleasedEnum.YES);
            stockOccupyMapper.updateById(stockOccupy);
        }

        return ResponseResult.success();
    }

    @Override
    public ResponseResult occupyStockRelease4AfterSaleOrder(StockOccupy4WarehouseDTO occupyDTO) {
        //仓库售后单审核占用释放(售后单回传运单号出库)
        LambdaQueryWrapper<StockOccupyDO> occupyWrapper = Wrappers.lambdaQuery();
        occupyWrapper.eq(StockOccupyDO::getAfterSaleOrderSn, occupyDTO.getOrderSn());
        occupyReleaseApply(occupyDTO, occupyWrapper);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult occupyStockRelease4Order(StockOccupy4WarehouseDTO occupyDTO) {
        //仓库订单出库占用释放 生成占用出库记录
        //查询占用记录
        StockOccupyOrderDO stockOccupy = stockOccupyOrderMapper.selectOne(
                Wrappers.<StockOccupyOrderDO>lambdaQuery()
                        .eq(StockOccupyOrderDO::getMaterialId, occupyDTO.getMaterialId())
                        .eq(StockOccupyOrderDO::getWarehouseId, occupyDTO.getWarehouseId())
                        .eq(StockOccupyOrderDO::getOrderSn, occupyDTO.getOrderSn())
                        .eq(StockOccupyOrderDO::getIsDel, IsDeletedEnum.NO)
        );
        //ExceptionEnum.FAIL_STOCK_OCCUPY_ORDER_RECORD_EXISTS.doThrowIf(null != stockOccupy);

        StockMaterialDO stockMaterial = getStockMaterialDO(occupyDTO.getMaterialId(), occupyDTO.getWarehouseId());
        ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);

        //物料商品占用更新
        assert stockMaterial != null;
        doReleaseOccupy(occupyDTO.getOccupyNum(), stockMaterial);

        //插入订单占用记录
        StockOccupyOrderDO stockOccupyDo = new StockOccupyOrderDO();
        stockOccupyDo.setMaterialId(occupyDTO.getMaterialId());
        stockOccupyDo.setGoodsId(stockMaterial.getGoodsId());
        stockOccupyDo.setWarehouseId(occupyDTO.getWarehouseId());
        stockOccupyDo.setOrderSn(occupyDTO.getOrderSn());
        stockOccupyDo.setOccupyNum(occupyDTO.getOccupyNum());
        stockOccupyOrderMapper.insert(stockOccupyDo);
        return ResponseResult.success();
    }

    /**
     * @Description: 查询物料库存记录
     * @Param materialId
     * @Param warehouseId
     * @Return {@link StockMaterialDO}
     * @Throws
     * @Author Abel Yang
     * @Date 2020/10/14/014 14:41
     */
    private StockMaterialDO getStockMaterialDO(Long materialId, Long warehouseId) {
        return stockMaterialMapper.selectOne(
                Wrappers.<StockMaterialDO>lambdaQuery()
                        .eq(StockMaterialDO::getMaterialId, materialId)
                        .eq(StockMaterialDO::getWarehouseId, warehouseId)
                        .eq(StockMaterialDO::getIsDel, IsDeletedEnum.NO)
        );
    }

    private void doReleaseOccupy(Integer occupyNum, StockMaterialDO stockMaterial) {
        //更新物料库存 加出库、减占用
        log.info("---------更新物料库存 加出库、减占用----[stockMaterial]:[{}]", JSON.toJSONString(stockMaterial));
        int materialOccupyLeft = stockMaterial.getOccupyNum() - occupyNum;
        ExceptionEnum.FAIL_STOCK_OCCUPY_MATERIAL_OCCUPY_NUM_NOT_ENOUGH.doThrowIf(materialOccupyLeft < 0);
        stockMaterialMapper.update(
                new StockMaterialDO(),
                Wrappers.<StockMaterialDO>lambdaUpdate()
                        .eq(StockMaterialDO::getId, stockMaterial.getId())
                        .setSql("occupy_num = occupy_num -" + occupyNum)
                        .setSql("out_stock_num = out_stock_num +" + occupyNum)
                        .setSql("real_stock_num = real_stock_num -" + occupyNum)
        );
    }

    /*售后出库走这里*/
    private void occupyReleaseApply(StockOccupy4WarehouseDTO occupyDTO,
                                    LambdaQueryWrapper<StockOccupyDO> occupyWrapper) {

        //查询占用记录
        log.info("---------售后出库----[查询占用记录查询前日志]:[{}]", JSON.toJSONString(occupyDTO));
        occupyWrapper.eq(StockOccupyDO::getMaterialId, occupyDTO.getMaterialId())
                .eq(StockOccupyDO::getWarehouseId, occupyDTO.getWarehouseId())
                .eq(StockOccupyDO::getIsReleased, StockOccupyIsReleasedEnum.NO)
                .eq(StockOccupyDO::getIsDel, IsDeletedEnum.NO);
        StockOccupyDO stockOccupy = stockOccupyMapper.selectOne(occupyWrapper);
        log.info("---------售后发货查询----[查询占用记录]:[{}]", JSON.toJSONString(stockOccupy));
        ExceptionEnum.FAIL_STOCK_OCCUPY_RECORD_NOT_EXISTS.doThrowIf(null == stockOccupy);

        //查询物料库存记录
        Integer originNum = 0;
        StockMaterialDO stockMaterial = getStockMaterialDO(occupyDTO.getMaterialId(), occupyDTO.getWarehouseId());
        log.info("---------售后发货查询----[查询物料库存记录]:[{}]", JSON.toJSONString(stockMaterial));
        ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);
        if (null != stockMaterial) {
            originNum = occupyDTO.getOccupyNum() > 0 ? stockMaterial.getRealStockNum() :
                    stockMaterial.getDamageStockNum();
        }
        log.info("---------售后发货查询----[originNum]:[{}]", JSON.toJSONString(originNum));
        //库存变动日志
        StockLogDO stockLogDO = new StockLogDO();
        stockLogDO.setMaterialId(stockMaterial.getMaterialId());
        stockLogDO.setGoodsId(stockMaterial.getGoodsId());
        stockLogDO.setWarehouseId(stockMaterial.getWarehouseId());
        stockLogDO.setChangeType(occupyDTO.getChangeType());
        stockLogDO.setChangeNum(occupyDTO.getOccupyNum());
        stockLogDO.setCreateBy(occupyDTO.getUpdateBy());
        stockLogDO.setUpdateBy(occupyDTO.getUpdateBy());

        log.info("---------售后发货查询----[getIsCancellationOrder]:[{}]",
                JSON.toJSONString(occupyDTO.getIsCancellationOrder()));
        if (null == occupyDTO.getIsCancellationOrder() || "".equals(occupyDTO.getIsCancellationOrder())) {
            try {
                Long valId = Long.parseLong(occupyDTO.getChangeType().toString());
                JmDictValue dictValue = dictValueService.findById(valId);
                String changeReason = dictValue.getValName();
                if (StringUtils.isBlank(changeReason)) {
                    stockLogDO.setChangeReason("不明原因");
                } else {
                    stockLogDO.setChangeReason(changeReason);
                }
            } catch (NumberFormatException e) {
                stockLogDO.setChangeReason("不明原因");
            }
            stockLogDO.setOriginNum(originNum);
            stockLogDO.setLogTypes((byte) 0);
        } else {
            //取消订单占用日志
            stockLogDO.setChangeReason("取消订单减占用");
            stockLogDO.setOriginNum(occupyDTO.getOccupyNum());
            stockLogDO.setLogTypes((byte) 1);
        }
        log.info("---------售后发货查询----[getChangeReason]:[{}]", JSON.toJSONString(stockLogDO.getChangeReason()));
        /*if(null != occupyDTO.getChangeReasonId()){
            stockLogDO.setBackup(stockLogDO.getChangeReason()+occupyDTO.getChangeReasonId());
        }*/
        stockLogMapper.insert(stockLogDO);
        log.info("---------物料商品占用更新----[传值日志]:[{}]", JSON.toJSONString(occupyDTO));
        if (null == occupyDTO.getIsUpdate() && null == occupyDTO.getIsCancellationOrder()) {
            //库存变动日志,如果是更新操作,并且不是取消订单操作
            //物料商品占用更新
            assert stockMaterial != null;
            assert stockOccupy != null;
            doReleaseOccupy(stockOccupy.getOccupyNum(), stockMaterial);
        } else {
            //编辑
            //物料商品占用更新
            assert stockMaterial != null;
            assert stockOccupy != null;
            doReleaseOccupyEdit(stockOccupy.getOccupyNum(), stockMaterial);
        }
        //清除占用记录上的占用标记
        stockOccupy.setIsReleased(StockOccupyIsReleasedEnum.YES);
        stockOccupyMapper.updateById(stockOccupy);

    }

    private void doReleaseOccupyEdit(Integer occupyNum, StockMaterialDO stockMaterial) {
        //更新物料库存减占用
        log.info("---------更新物料库存减占用----[stockMaterial]:[{}]", JSON.toJSONString(stockMaterial));
        int materialOccupyLeft = stockMaterial.getOccupyNum() - occupyNum;
        ExceptionEnum.FAIL_STOCK_OCCUPY_MATERIAL_OCCUPY_NUM_NOT_ENOUGH.doThrowIf(materialOccupyLeft < 0);
        stockMaterialMapper.update(
                new StockMaterialDO(),
                Wrappers.<StockMaterialDO>lambdaUpdate()
                        .eq(StockMaterialDO::getId, stockMaterial.getId())
                        .setSql("occupy_num = occupy_num -" + occupyNum)
                        .setSql("available_stock_num = available_stock_num +" + occupyNum)
        );
    }

    private void occupyReleaseDamage(StockOccupy4WarehouseDTO occupyDTO) {
        ExceptionEnum.FAIL_OCCUPY_PARAM_NOT_EMPTY.doThrowIf(null == occupyDTO.getOutStockApplyId());

        //查询物料库存记录
        StockMaterialDO stockMaterial = getStockMaterialDO(occupyDTO.getMaterialId(), occupyDTO.getWarehouseId());
        ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);
        //原库存
        Integer originNum = stockMaterial.getAvailableStockNum();
        //解除破损占用
        stockOccupyDamageMapper.update(new StockOccupyDamageDO(),
                Wrappers.<StockOccupyDamageDO>lambdaUpdate()
                        .eq(StockOccupyDamageDO::getMaterialId, occupyDTO.getMaterialId())
                        .eq(StockOccupyDamageDO::getWarehouseId, occupyDTO.getWarehouseId())
                        .eq(StockOccupyDamageDO::getOutStockApplyId, occupyDTO.getOutStockApplyId())
                        .eq(StockOccupyDamageDO::getIsDel, IsDeletedEnum.NO)
                        .set(StockOccupyDamageDO::getIsReleased, StockOccupyIsReleasedEnum.YES)
        );

        assert stockMaterial != null;
        stockMaterialMapper.update(
                new StockMaterialDO(),
                Wrappers.<StockMaterialDO>lambdaUpdate()
                        .eq(StockMaterialDO::getId, stockMaterial.getId())
                        .setSql("out_stock_num = out_stock_num +" + occupyDTO.getOccupyNum())
                        .setSql("real_stock_num = real_stock_num -" + occupyDTO.getOccupyNum())
        );
        //破损变动占用日志
        /*logAdd(stockMaterial.getMaterialId(), stockMaterial.getGoodsId(), stockMaterial.getWarehouseId(),
                occupyDTO.getChangeType(), occupyDTO.getOccupyNum(), (byte) 0, "",
                occupyDTO.getUpdateBy(), (byte) 1, originNum, "");*/
    }

    @Override
    public List<StockMaterialStockDTO> getMaterialStock(List<Long> materialIds, Long warehouseId) {
        List<StockMaterialStockDTO> result = Lists.newArrayList();

        QueryWrapper<StockMaterialDO> queryWrapper = new QueryWrapper<>();
        String select = "material_id,material_code,warehouse_id, sum(in_stock_num) as in_stock_num" +
                ", sum(out_stock_num) as out_stock_num,sum(real_stock_num) as real_stock_num" +
                ", sum(available_stock_num) as available_stock_num, sum(damage_stock_num) as damage_stock_num, sum" +
                "(occupy_num) as occupy_num";
        queryWrapper.select(select).lambda()
                .in(StockMaterialDO::getMaterialId, materialIds)
                .groupBy(StockMaterialDO::getMaterialId);
        if (null != warehouseId) {
            queryWrapper.lambda().eq(StockMaterialDO::getWarehouseId, warehouseId);
        }
        List<StockMaterialDO> materialStockList = stockMaterialMapper.selectList(queryWrapper);

        if (0 == materialStockList.size()) {
            return result;
        }

        materialStockList.forEach(materialDO -> {
            StockMaterialStockDTO stockMaterialStockDTO = new StockMaterialStockDTO();

            stockMaterialStockDTO.setMaterialId(materialDO.getMaterialId());
            stockMaterialStockDTO.setMaterialCode(materialDO.getMaterialCode());
            stockMaterialStockDTO.setWarehouseId(materialDO.getWarehouseId());

            stockMaterialStockDTO.setAvailableStockNum(materialDO.getAvailableStockNum());
            stockMaterialStockDTO.setDamageStockNum(materialDO.getDamageStockNum());
            result.add(stockMaterialStockDTO);
        });

        return result;
    }

    @Override
    public int commodityStockRelease(List<CommodityStockReleaseDTO> releaseList, String updateBy) {
        log.info("-------------[商城库存清除 原始数据]:[{}]", JSON.toJSONString(releaseList));
        ExceptionEnum.FAIL_STOCK_COMMODITY_RELEASE_LIST_NOT_EMPTY.doThrowIf(releaseList.size() <= 0);

        AtomicInteger rows = new AtomicInteger();
        releaseList.forEach(release -> {
            //查询库存是否充足
            StockMaterialDO stockMaterial = getStockMaterial(release.getMaterialId(), release.getWarehouseId());
            assert stockMaterial != null;
            int occupyLeft = stockMaterial.getOccupyNum() - release.getStockNum();
            log.info("-------------[商城库存清除 单个物料]:MaterialId[{}], material[{}]", release.getMaterialId(),
                    JSON.toJSONString(stockMaterial));
            ExceptionEnum.FAIL_STOCK_OCCUPY_RELEASE_NOT_ENOUGH.doThrowIf(occupyLeft < 0);

            Integer originNum = stockMaterial.getAvailableStockNum();

            LambdaUpdateWrapper<StockMaterialDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(StockMaterialDO::getId, stockMaterial.getId())
                    .setSql("occupy_num = occupy_num -" + release.getStockNum())
                    .setSql("available_stock_num = available_stock_num +" + release.getStockNum())
                    //判断是否触发预警
                    .set(StockMaterialDO::getAlarmed,
                            stockMaterial.getAlarmNum() > (stockMaterial.getAvailableStockNum() + release.getStockNum()));
            //更新物料库存
            stockMaterialMapper.update(new StockMaterialDO(), updateWrapper);

            //库存变动日志
            StockLogDO logDO = new StockLogDO();
            logDO.setMaterialId(stockMaterial.getMaterialId());
            logDO.setGoodsId(stockMaterial.getGoodsId());
            logDO.setWarehouseId(stockMaterial.getWarehouseId());
            logDO.setChangeType(StockChangeTypeEnum.TYPE_COMMODITY_RELEASE.getChangeType());
            logDO.setChangeNum(release.getStockNum());
            logDO.setCreateBy(updateBy);
            logDO.setUpdateBy(updateBy);
            logDO.setOriginNum(originNum);
            stockLogMapper.insert(logDO);

            rows.incrementAndGet();
        });

        return rows.get();
    }

    /**
     * @Description: 常规占用
     * @Param stockOccupyDO
     * @Return
     * @Throws
     * @Author Abel Yang
     * @Date 2020/9/24/024 15:36
     */
    private void normalOccupy(StockOccupyDO occupyDO, Integer changeType) {
        log.info("---------排查售后审核问题1----[常规占用传递值]:[{}]", JSON.toJSONString(occupyDO));
        //查询库存是否充足
        StockMaterialDO stockMaterial = getStockMaterial(occupyDO.getMaterialId(), occupyDO.getWarehouseId());
        assert stockMaterial != null;
        int availableStockNum = stockMaterial.getAvailableStockNum() - occupyDO.getOccupyNum();
        log.info("---------排查售后审核问题2----[物料可用库存-占用数量]:stockMaterial:[{}][{}]", JSON.toJSONString(stockMaterial),
                JSON.toJSONString(availableStockNum));
        ExceptionEnum.FAIL_STOCK_AVAILABLE_STOCK_NOT_ENOUGH.doThrowIf(availableStockNum < 0);

        //原库存
        Integer originNum = stockMaterial.getAvailableStockNum();
        LambdaUpdateWrapper<StockMaterialDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StockMaterialDO::getId, stockMaterial.getId())
                .setSql("occupy_num = occupy_num +" + occupyDO.getOccupyNum())
                .setSql("available_stock_num = available_stock_num -" + occupyDO.getOccupyNum())
                //判断是否触发预警
                .set(StockMaterialDO::getAlarmed, stockMaterial.getAlarmNum() > availableStockNum);
        //更新物料库存占用
        stockMaterialMapper.update(new StockMaterialDO(), updateWrapper);
        //生成占用库存
        occupyDO.setGoodsId(stockMaterial.getGoodsId());
        stockOccupyMapper.insert(occupyDO);
        log.info("---------排查售后审核问题3----[生成占用库存]:[{}]", JSON.toJSONString(occupyDO));
        String backUps = "";
        /*try {
            Long valId = Long.parseLong(changeType.toString());
            JmDictValue dictValue = dictValueService.findById(valId);
            String changeReason = dictValue.getValName();
            if (StringUtils.isBlank(changeReason)) {
                backUps = "不明原因"+stockLogIdentificationDO.getChangeReasonId();
            } else {
                backUps = changeReason+stockLogIdentificationDO.getChangeReasonId();
            }
        } catch (NumberFormatException e) {
            backUps = "不明原因"+stockLogIdentificationDO.getChangeReasonId();
        }*/
        //库存变动占用日志
        logAdd(stockMaterial.getMaterialId(), stockMaterial.getGoodsId(), stockMaterial.getWarehouseId(), changeType,
                occupyDO.getOccupyNum(), (byte) 1, "", occupyDO.getUpdateBy(), (byte) 1,
                stockMaterial.getOccupyNum(), backUps);

    }

    @Nullable
    private StockMaterialDO getStockMaterial(Long materialId, Long warehouseId) {
        StockMaterialDO stockMaterial = stockMaterialMapper.selectOne(
                Wrappers.<StockMaterialDO>lambdaQuery()
                        .eq(StockMaterialDO::getMaterialId, materialId)
                        .eq(StockMaterialDO::getWarehouseId, warehouseId)
                        .eq(StockMaterialDO::getIsDel, IsDeletedEnum.NO)
        );
        ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);
        return stockMaterial;
    }

    /**
     * @Description: 破损占用
     * @Param stockOccupyDO
     * @Return
     * @Throws
     * @Author Abel Yang
     * @Date 2020/9/24/024 15:44
     */
    private void damageOccupy(StockOccupy4WarehouseDTO occupyDTO, Integer changeType) {
        log.info("售后订单单个审核传值1---破损占用： [{}]", JSON.toJSONString(occupyDTO));
        //查询该次申请是否已经占用过
        StockOccupyDamageDO stockOccupy = stockOccupyDamageMapper.selectOne(
                Wrappers.<StockOccupyDamageDO>lambdaQuery()
                        .eq(StockOccupyDamageDO::getOutStockApplyId, occupyDTO.getOutStockApplyId())
                        .eq(StockOccupyDamageDO::getIsDel, IsDeletedEnum.NO)
        );
        log.info("售后订单单个审核传值2---stockOccupy： [{}]", JSON.toJSONString(stockOccupy));
        ExceptionEnum.FAIL_STOCK_OCCUPY_RECORD_EXISTS.doThrowIf(null != stockOccupy);

        //查询库存是否充足 可用库存 = 破损库存 - 占用库存
        StockMaterialDO stockMaterial = getStockMaterial(occupyDTO.getMaterialId(), occupyDTO.getWarehouseId());
        //原库存
        Integer originNum = stockMaterial.getAvailableStockNum();

        assert stockMaterial != null;
        int availableStockNum = stockMaterial.getDamageStockNum() - occupyDTO.getOccupyNum();
        ExceptionEnum.FAIL_STOCK_AVAILABLE_STOCK_NOT_ENOUGH.doThrowIf(availableStockNum < 0);

        //更新物料库存占用
        stockMaterialMapper.update(new StockMaterialDO(),
                Wrappers.<StockMaterialDO>lambdaUpdate().eq(StockMaterialDO::getId, stockMaterial.getId())
                        .setSql("damage_stock_num = damage_stock_num -" + occupyDTO.getOccupyNum())
        );
        //生成破损占用记录
        StockOccupyDamageDO occupyDamageDO = new StockOccupyDamageDO();
        occupyDamageDO.setMaterialId(occupyDTO.getMaterialId());
        occupyDamageDO.setGoodsId(stockMaterial.getGoodsId());
        occupyDamageDO.setWarehouseId(occupyDTO.getWarehouseId());
        occupyDamageDO.setOccupyNum(occupyDTO.getOccupyNum());
        occupyDamageDO.setCreateBy(occupyDTO.getUpdateBy());
        occupyDamageDO.setUpdateBy(occupyDTO.getUpdateBy());
        occupyDamageDO.setGoodsId(stockMaterial.getGoodsId());
        log.info("售后订单单个审核传值2---occupyDamageDO： [{}]", JSON.toJSONString(occupyDamageDO));
        stockOccupyDamageMapper.insert(occupyDamageDO);
        log.info("售后订单单个审核传值3---insert： [{}]", JSON.toJSONString("insert"));
        //库存变动占用日志
        logAdd(stockMaterial.getMaterialId(), stockMaterial.getGoodsId(), stockMaterial.getWarehouseId(), changeType,
                occupyDTO.getOccupyNum(), (byte) 1, "", occupyDTO.getUpdateBy(), (byte) 2, originNum, "");
    }

    public void logAdd(long materialId, long goodsId, long warehouseId, Integer changeType, Integer inStockNums,
                       Byte changeQuantityType, String batchCode, String updateBy, Byte logTypes, Integer originNum,
                       String backup) {
        StockLogDO logDO = new StockLogDO();
        logDO.setMaterialId(materialId);
        logDO.setGoodsId(goodsId);
        logDO.setWarehouseId(warehouseId);
        logDO.setChangeType(changeType);
        logDO.setChangeNum(inStockNums);
        logDO.setChangeQuantityType(changeQuantityType);
        if (batchCode != null && !batchCode.equals("")) {
            logDO.setChangeBatchNo(batchCode);
        }
        if (null == updateBy) {
            updateBy = "管理员";
        }
        logDO.setCreateBy(updateBy);
        logDO.setUpdateBy(updateBy);
        logDO.setLogTypes(logTypes);
        logDO.setOriginNum(originNum);
        if (backup != null && !backup.equals("")) {
            logDO.setBackup(backup);
        }
        try {
            Long valId = Long.parseLong(changeType.toString());
            JmDictValue dictValue = dictValueService.findById(valId);
            String changeReason = dictValue.getValName();
            if (StringUtils.isBlank(changeReason)) {
                logDO.setChangeReason("不明原因");
            } else {
                logDO.setChangeReason(changeReason);
            }
        } catch (NumberFormatException e) {
            logDO.setChangeReason("不明原因");
        }
        stockLogMapper.insert(logDO);
    }


}
